use crate::db::{dao, po};
use crate::dto;
use crate::service::error::Error;
use std::sync::Arc;
use wanmesh_common::error::ResponseError;

#[async_trait::async_trait]
pub trait EndpointPeerWireguardConfigService: Send + Sync {
    async fn add(
        &self,
        endpoint_peer_id: &str,
        params: &dto::core::endpoint_peer_wireguard_config::AddParams,
    ) -> Result<(), Error>;

    async fn get(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<dto::core::endpoint_peer_wireguard_config::EndpointPeerWireguardConfig, Error>;

    async fn try_get(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<Option<dto::core::endpoint_peer_wireguard_config::EndpointPeerWireguardConfig>, Error>;



    async fn update(
        &self,
        endpoint_peer_id: &str,
        params: &dto::core::endpoint_peer_wireguard_config::UpdateParams,
    ) -> Result<(), Error>;

    async fn delete(&self, endpoint_peer_id: &str) -> Result<(), Error>;

    async fn exists(
        &self,
        filter: &dto::core::endpoint_peer_wireguard_config::Filter,
    ) -> Result<bool, Error>;
}

pub struct DefaultEndpointPeerWireguardConfigService {
    endpoint_peer_wireguard_config_dao:
        Arc<dyn dao::endpoint_peer_wireguard_config::EndpointPeerWireguardConfigDao>,
}

impl DefaultEndpointPeerWireguardConfigService {
    pub fn new(
        endpoint_peer_wireguard_config_dao: Arc<
            dyn dao::endpoint_peer_wireguard_config::EndpointPeerWireguardConfigDao,
        >,
    ) -> Self {
        Self {
            endpoint_peer_wireguard_config_dao,
        }
    }
}

#[async_trait::async_trait]
impl EndpointPeerWireguardConfigService for DefaultEndpointPeerWireguardConfigService {
    async fn add(
        &self,
        endpoint_peer_id: &str,
        params: &dto::core::endpoint_peer_wireguard_config::AddParams,
    ) -> Result<(), Error> {
        let default_config = (po::ActiveValue::NotSet, po::ActiveValue::NotSet);

        let (wireguard_peer_endpoint, wireguard_peer_persistent_keepalive) =
            params.config.clone().map_or(default_config.clone(), |v| {
                v.peer.map_or(default_config.clone(), |v| {
                    (
                        po::option_to_active_value(v.endpoint),
                        po::option_to_active_value(v.persistent_keepalive),
                    )
                })
            });

        self.endpoint_peer_wireguard_config_dao
            .insert(po::endpoint_peer_wireguard_config::ActiveModel {
                id: Default::default(),
                endpoint_peer_id: po::ActiveValue::Set(endpoint_peer_id.to_string()),
                wireguard_peer_endpoint,
                wireguard_peer_persistent_keepalive,
                create_timestamp_millis: Default::default(),
                update_timestamp_millis: Default::default(),
            })
            .await?;
        Ok(())
    }

    async fn get(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<dto::core::endpoint_peer_wireguard_config::EndpointPeerWireguardConfig, Error> {
        let config = self.try_get(endpoint_peer_id).await?.ok_or(
            ResponseError::endpoint_peer_wireguard_config_not_found(endpoint_peer_id),
        )?;
        Ok(config)
    }

    async fn try_get(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<Option<dto::core::endpoint_peer_wireguard_config::EndpointPeerWireguardConfig>, Error>
    {
        let filter = dao::endpoint_peer_wireguard_config::Filter {
            endpoint_peer_id: Some(endpoint_peer_id.to_string()),
            ..Default::default()
        };

        if let Some(m) = self
            .endpoint_peer_wireguard_config_dao
            .try_get(filter)
            .await?
        {
            return Ok(Some(
                dto::core::endpoint_peer_wireguard_config::EndpointPeerWireguardConfig::from(&m),
            ));
        }

        Ok(None)
    }

    async fn update(
        &self,
        endpoint_peer_id: &str,
        params: &dto::core::endpoint_peer_wireguard_config::UpdateParams,
    ) -> Result<(), Error> {
        if params == &Default::default() {
            return Ok(());
        }

        let filter = dao::endpoint_peer_wireguard_config::Filter {
            endpoint_peer_id: Some(endpoint_peer_id.to_string()),
            ..Default::default()
        };

        if !self
            .endpoint_peer_wireguard_config_dao
            .exists(filter.clone())
            .await?
        {
            return Err(Error::ResponseError(
                ResponseError::endpoint_peer_wireguard_config_not_found(endpoint_peer_id),
            ));
        }

        self.endpoint_peer_wireguard_config_dao
            .update(filter.into(), params.into())
            .await?;

        Ok(())
    }

    async fn delete(&self, endpoint_peer_id: &str) -> Result<(), Error> {
        let filter = dao::endpoint_peer_wireguard_config::Filter {
            endpoint_peer_id: Some(endpoint_peer_id.to_string()),
            ..Default::default()
        };

        self.endpoint_peer_wireguard_config_dao
            .delete(filter)
            .await?;

        Ok(())
    }

    async fn exists(
        &self,
        filter: &dto::core::endpoint_peer_wireguard_config::Filter,
    ) -> Result<bool, Error> {
        let exists = self
            .endpoint_peer_wireguard_config_dao
            .exists(filter.into())
            .await?;
        Ok(exists)
    }
}

impl From<&po::endpoint_peer_wireguard_config::Model>
    for dto::core::endpoint_peer_wireguard_config::EndpointPeerWireguardConfig
{
    fn from(value: &po::endpoint_peer_wireguard_config::Model) -> Self {
        dto::core::endpoint_peer_wireguard_config::EndpointPeerWireguardConfig {
            config: dto::core::endpoint_peer_wireguard_config::WireguardConfig {
                peer: dto::core::endpoint_peer_wireguard_config::WireguardPeerConfig {
                    endpoint: value.wireguard_peer_endpoint.clone(),
                    persistent_keepalive: value.wireguard_peer_persistent_keepalive.clone(),
                },
            },
        }
    }
}

impl From<&dto::core::endpoint_peer_wireguard_config::Filter>
    for dao::endpoint_peer_wireguard_config::Filter
{
    fn from(value: &dto::core::endpoint_peer_wireguard_config::Filter) -> Self {
        Self {
            endpoint_peer_id: value.endpoint_peer_id.clone(),
        }
    }
}

impl From<&dto::core::endpoint_peer_wireguard_config::UpdateParams>
    for dao::endpoint_peer_wireguard_config::UpdateParams
{
    fn from(value: &dto::core::endpoint_peer_wireguard_config::UpdateParams) -> Self {
        if value == &Default::default() {
            return Self::default();
        }

        let wireguard_peer_endpoint = value.config.clone().map_or(po::ActiveValue::NotSet, |v| {
            v.peer.map_or(po::ActiveValue::NotSet, |v| {
                po::option_to_active_value(v.endpoint)
            })
        });

        let wireguard_peer_persistent_keepalive =
            value.config.clone().map_or(po::ActiveValue::NotSet, |v| {
                v.peer.map_or(po::ActiveValue::NotSet, |v| {
                    po::option_to_active_value(v.persistent_keepalive)
                })
            });

        Self {
            wireguard_peer_endpoint,
            wireguard_peer_persistent_keepalive,
        }
    }
}
